package com.nebula.common.datasource.redis.db;

import com.nebula.common.util.JsonEncoder;
import com.nebula.common.util.PropertyUtil;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedis;
import redis.clients.util.Pool;

import java.util.*;

public class RedisDB {
	private static final Logger LOGGER = LoggerFactory.getLogger(RedisDB.class);
	private static Map<String, Pool<ShardedJedis>> pools = new HashMap<>();
    //private Pool<ShardedJedis> jedisPool;
	private JedisPool jedisPool;//
	//private Jedis jedis;//临时

    public RedisDB(String name) {
        //this.jedisPool = getPool(name);

		JedisPoolConfig config = new JedisPoolConfig();
		//config.setMaxActive(20);
		config.setMaxIdle(5);
		//config.setMaxWait(1000l);
		config.setMaxWaitMillis(1000L);
		config.setTestOnBorrow(false);

		jedisPool = new JedisPool(config, PropertyUtil.getProperty("spring.redis.host"),
				Integer.valueOf(PropertyUtil.getProperty("spring.redis.port")));

    }

    private synchronized Pool<ShardedJedis> getPool(String name) {
        Pool<ShardedJedis> pool = pools.get(name);
        if (pool != null) {
            return pool;
        }
        
        /*RedisConfig.RedisInfo di = RedisConfig.get(name);
        int minPoolSize = di.getSettings().getInt32("MinPoolSize", 1);
        int maxPoolSize = di.getSettings().getInt32("MaxPoolSize", 100);
        String masterNamesConfig = di.getSettings().getString("MasterNames");
        int connectTimeout = di.getSettings().getInt32("ConnectTimeout", 5000);

		String password=di.getSettings().getString("password", "");
		long readTimeout=di.getSettings().getInt64("ReadTimeout", 10000);
		long writeTimeout=di.getSettings().getInt64("WriteTimeout", 10000);
		int minIdle=di.getSettings().getInt32("MinIdle", 10);
		int maxIdle=di.getSettings().getInt32("MaxIdle", 50);

        if (masterNamesConfig == null) {
            throw new FaultException("redis config missing MasterNames config");
        }
        Set<String> masterNames = new LinkedHashSet<>(Arrays.asList(masterNamesConfig.split(",")));
        Set<String> sentinels = new LinkedHashSet<>();
        List<RedisConfig.RedisNode> nodes = di.getServers();
        //Server node is sentinel config;
        for (RedisConfig.RedisNode node : nodes) {
            sentinels.add(String.format("%s:%s", node.getHost(), node.getPort()));
        }
        GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
		poolConfig.setMaxTotal(maxPoolSize);
		poolConfig.setMinIdle(minIdle);
		poolConfig.setMaxIdle(maxIdle);
		poolConfig.setMaxWaitMillis(writeTimeout);
		poolConfig.setTestOnBorrow(true);

		if(StringUtils.isEmpty(password)){
			pool= new ShardedJedisSentinelPool(masterNames, sentinels, poolConfig, connectTimeout);
		}else{
			pool= new ShardedJedisSentinelPool(masterNames, sentinels, poolConfig, connectTimeout, password);
		}
		 pools.put(name, pool);
		*/
        return pool;
    }

	
    /**
     * 设置缓存对象
     * @param key
     * @param value
     * @param secods
     */
    public void set(Object key,Object value,int secods){
    	
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()) {
    		String skey=String.valueOf(key);
    		jedis.set(skey, JsonEncoder.DEFAULT.encode(value));
            jedis.expire(skey, secods);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.set(key:{},value:{},secods:{}) {}",key,value,secods,e);
	    	throw e;
		}
    	
    	
    }
    
    
	
    /**
     * 设置值  keyPrefix+key 为存储的key
     * @param keyPrefix key前缀
     * @param key key值
     * @param value
     * @param secods
     */
    public void set(String keyPrefix ,Object key,Object value,int secods){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()) {
    			String skey=keyPrefix+"_"+String.valueOf(key);
            jedis.set(skey, JsonEncoder.DEFAULT.encode(value));
            jedis.expire(skey, secods);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.set(keyPrefix:{},key:{},value:{},secods:{}) {}",keyPrefix,key,value,secods,e);
          throw e;
		}
    }
    

    
    /**
     * 获取值keyPrefix+key 为存储的key
     * @param keyPrefix key前缀
     * @param key key值
     * @param clazz
     * @return
     */
    public <T> T get(String keyPrefix ,Object key,Class<T> clazz){
    	//ShardedJedis jedis = jedisPool.getResource();
		Jedis jedis = jedisPool.getResource();
		try{
    		 String skey=keyPrefix+"_"+String.valueOf(key);
    		 return JsonEncoder.DEFAULT.decode(jedis.get(skey), clazz);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.get(keyPrefix:{},key:{},clazz:{}) {}",keyPrefix,key,clazz.getSimpleName(),e);
	    	throw e;
		}finally {
			jedis.close(); // TODO 临时修改.测试性能
		}
    }
    
    /**
     * 取得缓存对象
     * @param key
     * @param clazz
     * @return
     */
    public <T> T get(Object key,Class<T> clazz){
    	//ShardedJedis jedis = jedisPool.getResource();
		Jedis jedis = jedisPool.getResource();
    	try{
    		String skey=String.valueOf(key);
    		return JsonEncoder.DEFAULT.decode(jedis.get(skey), clazz);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.get(key:{},clazz:{}) {}",key,clazz.getSimpleName(),e);
          throw e;
		}finally {
			jedis.close(); // TODO 临时修改.测试性能
		}
    }
    
    
	
    /**
     * 当不存在值，设置缓存
     * @param key   名称
     * @param value 缓存值
     * @param seconds 过期时间
     * @return
     */
    public boolean setnx(String key,Object value,int seconds){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    		 Long result = jedis.setnx(key, JsonEncoder.DEFAULT.encode(value));
             jedis.expire(key, seconds);
             return result == 1;
	    }catch (Exception e) {
	    	 LOGGER.error("ERROR-> RedisDB.get(key:{},value:{},seconds:{}) {}",key,value,seconds,e);
             throw e;
		}
    }
    
   
    

    
 
        
    /**
     * 设置值
     * @param key
     * @param field
     * @param value
     * @param secods
     */
    public void hset(String key, String field, String value,int secods){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
            jedis.hset(key, field, JsonEncoder.DEFAULT.encode(value));
            jedis.expire(key, secods);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hset(key:{},field:{},value:{},seconds:{}) {}",key,field,value,secods,e);
          throw e;
		}
    }
    /**
     * 设置值
     * @param keyPrefix
     * @param key
     * @param field
     * @param value
     * @param secods
     */
    public void hset(String keyPrefix,Object key, String field, Object value,int secods){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    			String skey=keyPrefix+"_"+String.valueOf(key);
            jedis.hset(skey, field, JsonEncoder.DEFAULT.encode(value));
            jedis.expire(skey, secods);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hset(keyPrefix:{},key:{},field:{},value:{},seconds:{}) {}",keyPrefix,key,field,value,secods,e);
          throw e;
		}
    }
    
   /**
    * 设置超时时间
    * @param keyPrefix
    * @param key
    * @param secods
    */
    public void expire(String keyPrefix,Object key,int secods){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
			String skey=keyPrefix+"_"+String.valueOf(key);
       	 	jedis.expire(skey, secods);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.expire(keyPrefix:{},key:{},secods:{}) {}",keyPrefix,key,secods,e);
          throw e;
		}
    	
    }
    
    
    /**
     * 取得值
     * @param key
     * @param field
     * @param clazz
     * @return
     */
    /*public <T> T hget(String key,String field,Class<T> clazz){
    	try(ShardedJedis jedis = jedisPool.getResource()) {
    		return JsonEncoder.DEFAULT.decode(jedis.hget(key,field), clazz);
	    }catch (Exception e) {
	    	LOGGER.error("hget failed error: {}",e);
          throw e;
		}
    }*/

    /**
     * 获取对象
     * @param keyPrefix
     * @param key
     * @param field
     * @param clazz
     * @return
     */
    public <T> T hget(String keyPrefix,Object key,String field,Class<T> clazz){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
			String skey=keyPrefix+"_"+String.valueOf(key);
    		return JsonEncoder.DEFAULT.decode(jedis.hget(skey,field), clazz);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hget(keyPrefix:{},key:{},field:{},clazz:{}) {}",keyPrefix,key,field,clazz.getSimpleName(),e);
          throw e;
		}
    
    }
    /**
     * 删除redis数据
     * @param keyPrefix
     * @param key
     */
    public void remove(String keyPrefix ,Object key){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    		 String skey=keyPrefix+"_"+String.valueOf(key);
    		 jedis.del(skey);
	    }catch (Exception e) {
	    	 LOGGER.error("ERROR-> RedisDB.remove(keyPrefix:{},key:{}) {}",keyPrefix,key,e);
          throw e;
		}
    	
    }
    /**
     * 删除数据
     * @param key
     */
    public void remove(String key){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    		 	 jedis.del(key);
	    }catch (Exception e) {
	    	 LOGGER.error("ERROR-> RedisDB.remove(key:{}) {}",key,e);
          throw e;
		}
    	
    }
    
    
    /**
     * 删除redis
     * @param key
     * @param field
     */
    public void hremove(String key ,String field){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    		 	 //String skey=keyPrefix+"_"+String.valueOf(key);
    		 jedis.hdel(key,field);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hremove(key:{},field:{}) {}",key,field,e);
          throw e;
		}
    }
    
    /**
     * 删除值
     * @param keyPrefix
     * @param key
     * @param field
     */
    public void hremove(String keyPrefix,Object key ,String field){
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
			String skey=keyPrefix+"_"+String.valueOf(key);
    		 jedis.hdel(skey,field);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hremove(key:{},field:{}) {}",key,field,e);
          throw e;
		}
    }


    

    
    
    /**
     * 哈希表中设置多个值
     * @param key   hashKey 名称
     * @param map 多个键值对
     * @param seconds 过期时间
     */
    public void hmset(String key, Map<String, String> map,int seconds) {
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    			jedis.hmset(key, map);
            jedis.expire(key, seconds);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hmset(key:{},map:{}) {}",key,map.size(),e);
         throw e;
		} 
    }
    
    
    /**
     * 获取过个缓存值
     * @param key hashKey 名称
     * @param fields 多个键
     * @return
     */
    public List<String> hmget(String key, String[] fields) {
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    			return jedis.hmget(key, fields);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hmget(key:{},fields:{}) {}",key,fields.length,e);
	    	throw e;
		} 
    }
    
    /**
     * 获取一个哈希表中的所有field名
     * @param key hashKey 名称
     * @return
     */
    public Set<String> hkeys(String key) {
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    			return jedis.hkeys(key);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hkeys(key:{}) {}",key,e);
	    	throw e;
		} 
    }
    
    /**
     * 取一个哈希表中的所有field名
     * @param keyPrefix
     * @param key
     * @return
     */
    public Set<String> hkeys(String keyPrefix,Object key) {
    	//try(ShardedJedis jedis = jedisPool.getResource()) {

		try(Jedis jedis = jedisPool.getResource()){
			String skey=keyPrefix+"_"+String.valueOf(key);

    		return jedis.hkeys(skey);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hkeys(key:{}) {}",key,e);
	    	throw e;
		} 
    }
    
    
    /**
     * 哈希表 key 中所有域的值
     * @param key hashKey 名称
     * @return 
     */
    public List<String> hvals(String key) {
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    		 	 return jedis.hvals(key);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hvals(key:{}) {}",key,e);
	    	throw e;
		} 
    }
    
    /**
     * 返回哈希表 key 中，所有的域和值
     * @param key key hashKey 名称
     * @return
     */
    public Map<String, String> hgetAll(String key) {
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    			return jedis.hgetAll(key);
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hgetAll(key:{}) {}",key,e);
	    	throw e;
		} 
    }
    
    /**
     * 返回哈希表 key 中，所有的域和值
     * @param keyPrefix
     * @param key
     * @return
     */
    public <T> Map<String,String> hgetAll(String keyPrefix,Object key) {
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    			String skey=keyPrefix+"_"+String.valueOf(key);
    		
    		Map<String,String> map=jedis.hgetAll(skey);
    		return map;
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.hgetAll(keyPrefix:{},key:{}) {}",keyPrefix,key,e);
	    	throw e;
		} 
    }
    
    
    /**
     * 计数器
     * @param key
     * @param seconds
     * @return
     */
    public long inc(String key,int seconds){
    	
    	//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
    			boolean exist = jedis.exists(key);
            long count = jedis.incr(key);
            if(!exist){
                jedis.expire(key, seconds);
            }
            return count;
	    }catch (Exception e) {
	    	LOGGER.error("inc failed error: {}",e);
	    	throw e;
		}
    	
    }
    
    /**
     * 递减
     * @param key
     * @param seconds
     * @return
     */
	public long decr(String key, int seconds) {
		//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
				Long val = this.get(key,Long.class);
			if (val > 0) {
				return jedis.decr(key);
			}
			return 0;
	    }catch (Exception e) {
	    	LOGGER.error("decr failed error: {}",e);
	    	throw e;
		}
		
		
		
	}
	/**
	 * redis 分布式锁 解决并发问题
	 * 当key 存在 返回 0  当key 不存在 返回1
	 * 增加 5 秒超时时间
	 * @author tian.luan@mtime.com.cn
	 */
	public long setnx(String value){
		//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
				Long val = jedis.setnx(value, value);
			jedis.expire(value,5);
			return val;
	    }catch (Exception e) {
	    	LOGGER.error("setnx failed error: {}",e);
	    	throw e;
		}
	} 
	/**
	  * 判断key是否存在
	  * @return boolean
	  * */
	 public boolean exists(String key){
		//try(ShardedJedis jedis = jedisPool.getResource()) {
		 try(Jedis jedis = jedisPool.getResource()){
		 		boolean exis=false;
			exis = jedis.exists(key);
			return exis;
	    }catch (Exception e) {
	    	LOGGER.error("ERROR-> RedisDB.exists(key:{}) {}",key,e);
	    	throw e;
		}
	 }
	 
	 
	 public long  incrby(String key,int value,int second){
		 //try(ShardedJedis jedis = jedisPool.getResource()) {
		 try(Jedis jedis = jedisPool.getResource()){
			  boolean exist = jedis.exists(key);
	            long count = jedis.incrBy(key, value);
	            if(!exist){
	            	// 默认存储10秒
	                jedis.expire(key, second);
	            }
	            return count;
		 }
		 catch (Exception e) {
		    	LOGGER.error("ERROR-> incrby(key:{}) {}",key,e);
		    	throw e;
			}
	 }
	/**
	 * redis 分布式锁 解决并发问题
	 * 当key 存在 返回 0  当key 不存在 返回1
	 * 增加 5 秒超时时间
	 * @author tian.luan@mtime.com.cn
	 */
	public long setnx(String value,int second){
		//try(ShardedJedis jedis = jedisPool.getResource()) {
		try(Jedis jedis = jedisPool.getResource()){
				Long val = jedis.setnx(value, value);
			jedis.expire(value,second);
			return val;
		}catch (Exception e) {
			LOGGER.error("setnx failed error: {}",e);
			throw e;
		}
	}



}
